package com.abe.gcore.base.http;

import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.abe.gcore.base.http.cookies.CookieManager;
import com.abe.gcore.utils.NetWorkUtils;
import com.abe.gcore.utils.Utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.io.IOException;
import java.lang.reflect.Type;
import java.security.SecureRandom;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/*************************缓存设置*********************
 1. noCache 不使用缓存，全部走网络
 2. noStore 不使用缓存，也不存储缓存
 3. onlyIfCached 只使用缓存
 4. maxAge 设置最大失效时间，失效则不使用 需要服务器配合
 5. maxStale 设置最大失效时间，失效则不使用 需要服务器配合 感觉这两个类似 还没怎么弄清楚，清楚的同学欢迎留言
 6. minFresh 设置有效时间，依旧如上
 7. FORCE_NETWORK 只走网络
 8. FORCE_CACHE 只走缓存*/
public class BaseApi {
    //读超时长，单位：毫秒
    private static final int READ_TIME_OUT = 767600;
    //连接时长，单位：毫秒
    private static final int CONNECT_TIME_OUT = 767600;
    //设缓存有效期为两天
    private static final long CACHE_STALE_SEC = 60 * 60 * 24 * 2;
    //查询缓存的Cache-Control设置，为if-only-cache时只查询缓存而不会请求服务器，max-stale可以配合设置缓存失效时间
    //max-stale 指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值，那么客户机可接收超出超时期指定值之内的响应消息。
    private static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC;
    //查询网络的Cache-Control设置，头部Cache-Control设为max-age=0
    //(假如请求了服务器并在a时刻返回响应结果，则在max-age规定的秒数内，浏览器将不会发送对应的请求到服务器，数据由缓存直接返回)时则不会使用缓存而请求服务器
    private static final String CACHE_CONTROL_AGE = "max-age=0";
    private static HttpLoggingInterceptor logInterceptor = null;
    private static Interceptor headerInterceptor = null;
    private static OkHttpClient okHttpClient = null;

    public static Retrofit retrofit(String mBaseUrl) {
        return retrofit(mBaseUrl, null, true);
    }

    public static Retrofit retrofit(String mBaseUrl, JsonDeserializer<Date> ds) {
        return retrofit(mBaseUrl, ds, false);
    }

    private static Retrofit retrofit(String mBaseUrl, JsonDeserializer<Date> ds, boolean isNormalDate) {
        initHttpClient();
        //Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();
        GsonBuilder builder = new GsonBuilder();
        if (isNormalDate) {
            builder.setDateFormat("yyyy-MM-dd HH:mm:ss");
        } else {
            ds = (ds == null) ? new DateDeserializer() : ds;
            builder.registerTypeAdapter(Date.class, ds);
        }
        Gson gson = builder.serializeNulls().create();
        return new Retrofit.Builder()
                .client(okHttpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(mBaseUrl)
                .build();
    }

    private static void initHttpClient() {
        //开启Log
        if (logInterceptor == null) {
            logInterceptor = new HttpLoggingInterceptor();
            logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        }
        //增加头部信息
        if (headerInterceptor == null) {
            headerInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request build = chain.request().newBuilder()
                            .addHeader("Content-Type", "application/json")
                            .build();
                    return chain.proceed(build);
                }
            };
        }
        //https
        final TrustManager[] trustManager = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {

                    }

                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {

                    }

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[0];
                    }
                }
        };
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustManager, new SecureRandom());
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            if (okHttpClient == null) {
                okHttpClient = new OkHttpClient.Builder()
                        .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
                        .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
                        .addInterceptor(new BaseOKHttpInterceptor())
                        .addNetworkInterceptor(mRewriteCacheControlInterceptor)
                        .cookieJar(new CookieManager(Utils.getContext()))
                        .addInterceptor(headerInterceptor)
                        .sslSocketFactory(sslSocketFactory)
                        .build();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (okHttpClient == null) {
            okHttpClient = new OkHttpClient.Builder()
                    .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
                    .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
                    .addInterceptor(new BaseOKHttpInterceptor())
                    .addNetworkInterceptor(mRewriteCacheControlInterceptor)
                    .cookieJar(new CookieManager(Utils.getContext()))
                    .addInterceptor(headerInterceptor)
                    .build();
        }
    }

    //根据网络状况获取缓存的策略
    @NonNull
    public static String getCacheControl() {
        return NetWorkUtils.isNetConnected(Utils.getContext()) ? CACHE_CONTROL_AGE : CACHE_CONTROL_CACHE;
    }

    /**
     * 云端响应头拦截器，用来配置缓存策略
     * Dangerous interceptor that rewrites the server's cache-control header.
     */
    private final static Interceptor mRewriteCacheControlInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            String cacheControl = request.cacheControl().toString();
            if (!NetWorkUtils.isNetConnected(Utils.getContext())) {
                request = request.newBuilder()
                        .cacheControl(TextUtils.isEmpty(cacheControl) ? CacheControl.FORCE_NETWORK : CacheControl.FORCE_CACHE)
                        .build();
            }
            Response originalResponse = chain.proceed(request);
            if (NetWorkUtils.isNetConnected(Utils.getContext())) {
                //有网的时候读接口上的@Headers里的配置，你可以在这里进行统一的设置
                return originalResponse.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
            } else {
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + CACHE_STALE_SEC)
                        .removeHeader("Pragma")
                        .build();
            }
        }
    };

    @SuppressLint("UseValueOf")
    static class DateDeserializer implements JsonDeserializer<Date> {
        @Override
        public Date deserialize(JsonElement json, Type typeOfT,
                                JsonDeserializationContext context) throws JsonParseException {
            String JSONDateToMilliseconds = "\\/(Date\\((.*?)(\\+.*)?\\))\\/";
            Pattern pattern = Pattern.compile(JSONDateToMilliseconds);
            Matcher matcher = pattern.matcher(json.getAsJsonPrimitive()
                    .getAsString());
            String result = matcher.replaceAll("$2");
            return new Date(new Long(result));
        }
    }

    class UtilDateSerializer implements JsonSerializer<Date> {
        @Override
        public JsonElement serialize(Date src, Type typeOfSrc,
                                     JsonSerializationContext context) {
            return new JsonPrimitive("/Date(" + src.getTime() + "+0800)/");
        }
    }
}

